/*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.http.converter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
import org.springframework.integration.http.multipart.DefaultMultipartFileReader;
import org.springframework.integration.http.multipart.MultipartFileReader;
import org.springframework.integration.http.multipart.MultipartHttpInputMessage;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.multipart.MultipartFile;
/**
* An {@link HttpMessageConverter} implementation that delegates to an instance of
* {@link AllEncompassingFormHttpMessageConverter} while adding the capability to <i>read</i>
* <code>multipart/form-data</code> content in an HTTP request.
*
* @author Mark Fisher
* @author Gary Russell
* @since 2.0
*/
public class MultipartAwareFormHttpMessageConverter implements HttpMessageConverter<MultiValueMap<String, ?>> {
private volatile MultipartFileReader<?> multipartFileReader = new DefaultMultipartFileReader();
private final AllEncompassingFormHttpMessageConverter wrappedConverter = new AllEncompassingFormHttpMessageConverter();
/**
* Sets the character set used for writing form data.
*
* @param charset The charset.
*/
public void setCharset(Charset charset) {
this.wrappedConverter.setCharset(charset);
}
/**
* Specify the {@link MultipartFileReader} to use when reading {@link MultipartFile} content.
*
* @param multipartFileReader The multipart file reader.
*/
public void setMultipartFileReader(MultipartFileReader<?> multipartFileReader) {
Assert.notNull(multipartFileReader, "multipartFileReader must not be null");
this.multipartFileReader = multipartFileReader;
}
@Override
public List<MediaType> getSupportedMediaTypes() {
return this.wrappedConverter.getSupportedMediaTypes();
}
@Override
public boolean canRead(Class<?> clazz, MediaType mediaType) {
if (!(MultiValueMap.class.isAssignableFrom(clazz) || byte[].class.isAssignableFrom(clazz))) {
return false;
}
if (mediaType != null) {
return MediaType.APPLICATION_FORM_URLENCODED.includes(mediaType)
|| MediaType.MULTIPART_FORM_DATA.includes(mediaType);
}
else {
return true;
}
}
@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return this.wrappedConverter.canWrite(clazz, mediaType);
}
@Override
public MultiValueMap<String, ?> read(Class<? extends MultiValueMap<String, ?>> clazz,
HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
MediaType contentType = inputMessage.getHeaders().getContentType();
if (!MediaType.MULTIPART_FORM_DATA.includes(contentType)) {
return this.wrappedConverter.read(clazz, inputMessage);
}
Assert.state(inputMessage instanceof MultipartHttpInputMessage,
"A request with 'multipart/form-data' Content-Type must be a MultipartHttpInputMessage. "
+ "Be sure to provide a 'multipartResolver' bean in the ApplicationContext.");
MultipartHttpInputMessage multipartInputMessage = (MultipartHttpInputMessage) inputMessage;
return this.readMultipart(multipartInputMessage);
}
private MultiValueMap<String, ?> readMultipart(MultipartHttpInputMessage multipartRequest) throws IOException {
MultiValueMap<String, Object> resultMap = new LinkedMultiValueMap<String, Object>();
Map<?, ?> parameterMap = multipartRequest.getParameterMap();
for (Entry<?, ?> entry : parameterMap.entrySet()) {
resultMap.add((String) entry.getKey(), entry.getValue());
}
for (Map.Entry<String, MultipartFile> entry : multipartRequest.getFileMap().entrySet()) {
MultipartFile multipartFile = entry.getValue();
if (multipartFile.isEmpty()) {
continue;
}
resultMap.add(entry.getKey(), this.multipartFileReader.readMultipartFile(multipartFile));
}
return resultMap;
}
@Override
public void write(MultiValueMap<String, ?> map, MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
this.wrappedConverter.write(map, contentType, outputMessage);
}
}